/*
 *  Copyright (C) 2009 Antonio Mata Chamorro
 *  
 *  Este programa es software libre: usted puede redistribuirlo y/o modificarlo
 *  bajo los t�rminos de la Licencia P�blica General GNU publicada
 *  por la Fundaci�n para el Software Libre, ya sea la versi�n 3
 *  de la Licencia, o (a su elecci�n) cualquier versi�n posterior.
 *  
 *  Este programa se distribuye con la esperanza de que sea �til, pero 
 *  SIN GARANT�A ALGUNA; ni siquiera la garant�a impl�cita
 *  MERCANTIL o de APTITUD PARA UN PROP�SITO DETERMINADO. 
 *  Consulte los detalles de la Licencia P�blica General GNU para obtener 
 *  una informaci�n m�s detallada. 
 *  
 *  Deber�a haber recibido una copia de la Licencia P�blica General GNU
 *  junto a este programa. 
 *  En caso contrario, consulte <http://www.gnu.org/licenses/>.
 *   
 */
package org.javahispano.javacup.tacticas_aceptadas.amata.com.amata.javacup.tacticas;

import java.util.LinkedList;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;

/**
 * Esta clase contiene los equipos que se enfrentan en un partido de f�tbol asi
 * como las herramientas que necesitan estos equipos para jugar. Estas
 * herramientas son dos SpotCalculator para que los jugadores sepan cuales son
 * las mejores posiciones de pase o autopase y un StrikeCalculator para saber
 * como disparar a puerta.
 * <p/>
 * Cada partido contiene una configuracion que determina como se comportaran los
 * equipos y jugadores.
 * <p/>
 * Cada llamada al m�todo update() calcula las mejores acci�nes a realizar por
 * cada jugador del equipo propio para ganar el partido.
 * 
 * @author Antonio Mata Chamorro
 * */
public class SoccerMatch {
	private SoccerMatchConfiguration configuration;

	private SoccerTeam ownTeam;
	private SoccerTeam opponentTeam;

	private SpotCalculator BSSCalculator;
	private SpotCalculator BAPSCalculator;
	private StrikeCalculator strikeCalculator;
	private Posicion ballPosicion;
	private boolean isOwnControllingBall;
	private SoccerPlayer lastControllerPlayer;

	/**
	 * Se crea un partido con la configuracion por defecto
	 * */
	public SoccerMatch() {
		this(new SoccerMatchConfiguration());
	}

	/**
	 * Crea un partido con cierta configuracion
	 * 
	 * @param configuration
	 *            configuracion que determinara como se comportaran los equipos
	 *            y jugadores
	 * */
	public SoccerMatch(SoccerMatchConfiguration configuration) {
		this.configuration = configuration;
		this.strikeCalculator = new StrikeCalculator(configuration);
		this.BAPSCalculator = new AutoPassSpotCalculator(configuration);
	}

	/**
	 * Devuelve una instancia de la clase SupportSpotCalculator, que permite
	 * calcular los mejores puntos para el siguiente pase.
	 * 
	 * @see SupportSpotCalculator
	 * */
	public SpotCalculator getBSSCalculator() {
		return BSSCalculator;
	}

	/**
	 * Devuelve una instancia de la clase AutoPassSpotCalculator, que permite
	 * calcular los mejores autopases que puede dar un jugador
	 * 
	 * @see AutoPassSpotCalculator
	 * */
	public SpotCalculator getBAPSCalculator() {
		return BAPSCalculator;
	}

	/**
	 * Devuelve una instancia de StrikeCalculator que permite saber el mejor
	 * tiro que un jugador puede realizar a puerta
	 * 
	 * @see StrikeCalculator
	 * */
	public StrikeCalculator getStrikeCalculator() {
		return strikeCalculator;
	}

	public void setOwnTeam(SoccerTeam ownTeam) {
		this.ownTeam = ownTeam;
		this.BSSCalculator = new SupportSpotCalculator(ownTeam,
				strikeCalculator);
	}

	public void setOpponentTeam(SoccerTeam opponentTeam) {
		this.opponentTeam = opponentTeam;
	}

	public SoccerTeam getOpponentTeam() {
		return opponentTeam;
	}

	/**
	 * Posicion actual de la pelota de juego en el campo
	 * */
	public Posicion getBallPosicion() {
		return ballPosicion;
	}

	/**
	 * Primero es necesario determinar que equipo controla el bal�n para saber
	 * que estrategia adoptara el equipo. <br/>
	 * 
	 * Despu�s se llama a SoccerTeam.update() para que el equipo propio ordene
	 * instrucciones a los jugadores seg�n la estrategia actual.<br/>
	 * 
	 * Finalmente se llama al m�todo SoccerPlayer.update() de cada uno de los
	 * jugadores propios para que ejecuten las ordenes recibidas desde el
	 * equipo.
	 * */
	public void update(SituacionPartido sp, LinkedList<Comando> comandos) {
		updatePositions(sp);

		// calculamos cual es el jugador propio que toco por ultima vez la
		// pelota
		ownTeam.setAttacker(null);
		int lastOwn = indexMaxValue(sp.iteracionesParaRematar());
		int maxOwnValue = 0;
		if (lastOwn >= 0) {
			ownTeam.setAttacker(this.ownTeam.getPlayers().get(lastOwn));
			maxOwnValue = sp.iteracionesParaRematar()[lastOwn];
		}

		// calculamos quien es el jugador contrario que toco por ultima vez la
		// pelota
		opponentTeam.setAttacker(null);
		int lastOpponent = indexMaxValue(sp.iteracionesParaRematarRival());
		int maxOpponentValue = 0;
		if (lastOpponent >= 0) {
			opponentTeam.setAttacker(this.opponentTeam.getPlayers().get(
					lastOpponent));
			maxOpponentValue = sp.iteracionesParaRematarRival()[lastOpponent];
		}

		// se controla el bal�n si un jugador de tu equipo fue el �ltimo en
		// tocar el bal�n
		if (maxOwnValue > maxOpponentValue) {
			this.lastControllerPlayer = ownTeam.getAttacker();
			this.isOwnControllingBall = true;
		} else if (maxOpponentValue > maxOwnValue) {
			this.lastControllerPlayer = opponentTeam.getAttacker();
			this.isOwnControllingBall = false;
		} else if (maxOpponentValue == maxOwnValue && sp.saco()) {
			this.isOwnControllingBall = true;
		} else if (maxOpponentValue == maxOwnValue && sp.sacaRival()) {
			this.isOwnControllingBall = false;
		}

		// log.info("isOwnControllingBall=" + isOwnControllingBall );

		this.ownTeam.update(sp, comandos);

		// los jugadores interpretan lo que deben hacer con las ordenes dadas
		// (�ltimo el attacker)
		for (SoccerPlayer player : this.ownTeam.getPlayers()) {
			if (player != this.ownTeam.getAttacker())
				player.update(sp, comandos);
		}
		this.ownTeam.getAttacker().update(sp, comandos);

		// log.info(this.ownTeam.toString());
	}

	private int indexMaxValue(int[] array) {
		int index = -1;

		for (int i = 0; i < array.length; i++) {
			if (array[index == -1 ? 0 : index] < array[i])
				index = i;
		}

		return index;
	}

	private void updatePositions(SituacionPartido sp) {
		this.ballPosicion = sp.balon();

		for (int i = 0; i < sp.misJugadores().length; i++) {
			this.ownTeam.getPlayers().get(i).setPosicion(sp.misJugadores()[i]);
		}

		for (int i = 0; i < sp.rivales().length; i++) {
			this.opponentTeam.getPlayers().get(i).setPosicion(sp.rivales()[i]);
		}

		this.ownTeam.setPlayersPositions(sp.misJugadores());
	}

	/**
	 * Indica si el equipo actual controla el bal�n
	 * 
	 * @return si el equipo actual controla el bal�n devuelve true, false en
	 *         caso contrario
	 * */
	public boolean isOwnControllingBall() {
		return isOwnControllingBall;
	}

	/**
	 * �ltimo jugador que controlo la pelota, del equipo propio o del contrario
	 * */
	public SoccerPlayer getLastControllerPlayer() {
		return lastControllerPlayer;
	}

	public void setConfiguration(SoccerMatchConfiguration configuration) {
		this.configuration = configuration;
	}

	public SoccerMatchConfiguration getConfiguration() {
		return configuration;
	}

}
